#ifndef RESOURCE_H
#define RESOURCE_H

#include <Globals.h>
#include <Map.h>

/**
  This is an empty class that other classes must derive from
  in order to have their functions called by a resource.
  
  NOTE: Any class which extends this MUST extend it as it's FIRST subclass, i.e.
    class A: public Callable, public B {
  NOT
    class A: public B, public Callable {
  This is for downcasting reasons.
**/
class Callable
{
public:
  Callable() {}
  ~Callable() {}
};

class Resource
{
public:
  typedef int (Callable::*Call0)();
  typedef int (Callable::*Call1)(int);
  typedef int (Callable::*Call2)(int,int);
  typedef int (Callable::*Call3)(int,int,int);
  typedef int (Callable::*Call4)(int,int,int,int);
  typedef int (Callable::*Call5)(int,int,int,int,int);

  Resource(Callable *target, const char *name=NULL);
  virtual ~Resource();
  
  /**
    Sets the target of all calls.
  **/
  void setTarget(Callable *t)
  {
    target = t;
  }
  Callable *getTarget()
  {
    return target;
  }
  
  void setId(u32int n)
  {
    resourceId = n;
  }
  u32int getId()
  {
    return resourceId;
  }
  
  /**
    Adds a function to the call table with the given ID.
  **/
  void add(Call0 call, u32int id);
  void add(Call1 call, u32int id);
  void add(Call2 call, u32int id);
  void add(Call3 call, u32int id);
  void add(Call4 call, u32int id);
  void add(Call5 call, u32int id);
  
  /**
    Removes the function with the given ID from the call table.
  **/
  void remove(u32int id);
  
  /**
    Calls the function with the given ID. This is called by the kernel.
    Calling from user mode will force an exception.
  **/
  virtual int callSys(u32int id, bool synchronous, int p1=0, int p2=0, int p3=0, int p4=0, int p5=0);

  virtual u32int getOwner()
  {
    return 0;
  }
  
protected:

  /**
    Given an ID, checks if it is currently in any call table.
  **/
  bool isMapped(u32int id);

  typedef Map<u32int,Call0,5> Call0Table;
  typedef Map<u32int,Call1,5> Call1Table;
  typedef Map<u32int,Call2,5> Call2Table;
  typedef Map<u32int,Call3,5> Call3Table;
  typedef Map<u32int,Call4,5> Call4Table;
  typedef Map<u32int,Call5,5> Call5Table;
  Call0Table table0;
  Call1Table table1;
  Call2Table table2;
  Call3Table table3;
  Call4Table table4;
  Call5Table table5;
  
  Callable *target;
  
  /**
    The ID of this resource, given by a ResourceManager.
  **/
  u32int resourceId;
};

/**
  This is the common syscall code which passes control to the kernel.
**/
int callResourceUser(u32int rId, u32int fId, int p1=0, int p2=0, int p3=0, int p4=0);
int callResourceUserAsync(u32int rId, u32int fId, int p1=0, int p2=0, int p3=0, int p4=0);

/**
  This is only usable for code which is already in kernel mode (I.e. doesn't need a context switch)
**/
int callResourceSys(u32int rId, u32int fId, int p1=0, int p2=0, int p3=0, int p4=0,int p5=0);
int callResourceSysAsync(u32int rId, u32int fId, int p1=0, int p2=0, int p3=0, int p4=0,int p5=0);

/**
  Calls the function with the given function ID on the resource with ID rId. This is called from user mode.
**/
template <class P1,class P2,class P3,class P4>
int callResource(u32int rId, const int fId, P1 p1=(P1)0, P2 p2=(P2)0, P3 p3=(P3)0, P4 p4=(P4)0)
{
  return callResourceUser(rId, (u32int)fId, (int)p1, (int)p2, (int)p3, (int)p4);
}

template <class P1,class P2,class P3>
int callResource(u32int rId, const int fId, P1 p1=(P1)0, P2 p2=(P2)0, P3 p3=(P3)0)
{
  return callResourceUser(rId, (u32int)fId, (int)p1, (int)p2, (int)p3);
}

template <class P1,class P2>
int callResource(u32int rId, const int fId, P1 p1=(P1)0, P2 p2=(P2)0)
{
  return callResourceUser(rId, (u32int)fId, (int)p1, (int)p2);
}

template <class P1>
int callResource(u32int rId, const int fId, P1 p1=(P1)0)
{
  return callResourceUser(rId, (u32int)fId, (int)p1);
}

int callResource(u32int rId, const int fId);

template <class P1,class P2,class P3,class P4>
int callResourceAsync(u32int rId, const int fId, P1 p1=(P1)0, P2 p2=(P2)0, P3 p3=(P3)0, P4 p4=(P4)0)
{
  return callResourceUserAsync(rId, (u32int)fId, (int)p1, (int)p2, (int)p3, (int)p4);
}

template <class P1,class P2,class P3>
int callResourceAsync(u32int rId, const int fId, P1 p1=(P1)0, P2 p2=(P2)0, P3 p3=(P3)0)
{
  return callResourceUserAsync(rId, (u32int)fId, (int)p1, (int)p2, (int)p3);
}

template <class P1,class P2>
int callResourceAsync(u32int rId, const int fId, P1 p1=(P1)0, P2 p2=(P2)0)
{
  return callResourceUserAsync(rId, (u32int)fId, (int)p1, (int)p2);
}

template <class P1>
int callResourceAsync(u32int rId, const int fId, P1 p1=(P1)0)
{
  return callResourceUserAsync(rId, (u32int)fId, (int)p1);
}

int callResourceAsync(u32int rId, const int fId);

/**
  Requests a resource. Returns -1 on failure.
**/
u32int requestResource(const char *name);

#endif
